import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


public class Grid 
{
	public static final byte SIZE = 8;
	public static final int CELL_COUNT = 64;
	
	public static final byte EMPTY = 0;
	public static final byte WHITE = 1;
	public static final byte BLACK = 2;
	
	private long top = -6148914694099828736L;
	//private long top = -6148914692668172971L;
	private long bottom = 1431655765L;
	private final byte currentPlayer;
	
	public Grid(byte currentPlayer)
	{
		this.currentPlayer = currentPlayer;
	}
	
	public Grid(byte currentPlayer, String[] initialValues)
	{
		this.currentPlayer = currentPlayer;
		generate(initialValues);
	}
	
	public Grid(Grid grid)
	{
		this.currentPlayer = grid.getCurrentPlayer();
		this.top = grid.getTop();
		this.bottom = grid.getBottom();
	}
	
	private void generate(String[] initialValues)
	{
		bottom = 0;
		top = 0;
		
		for(int i = 0; i < CELL_COUNT; i++)
		{
			if(initialValues[CELL_COUNT - 1 - i].equals("2"))
			{
				setCell((i << 1), BLACK);
			}
			else if(initialValues[CELL_COUNT - 1 - i].equals("4"))
			{
				setCell((i << 1), WHITE);
			}
			else
			{
				setCell((i << 1), EMPTY);
			}
		}
	}
	
	public long getTop()
	{
		return top;
	}
	
	public long getBottom()
	{
		return bottom;
	}
	
	public byte getCell(int position)
	{
		if(position < CELL_COUNT)
		{
			return (byte) ((bottom >> position) & 3);
		}
		else
		{
			return (byte) ((top >> (position - CELL_COUNT)) & 3);
		}
	}
	
	public void setCell(int position, long status)
	{	
		if(position < CELL_COUNT)
		{
			bottom &= ~(3L << position);
			
			if(status != 0)
			{
				bottom |= (status << position);
			}
		}
		else
		{
			top &= ~(3L << (position - CELL_COUNT));
			
			if(status != 0)
			{
				top |= (status << (position - CELL_COUNT));
			}
		}
	}
	
	public byte getCurrentPlayer()
	{
		return currentPlayer;
	}
	
	public List<String> getPlayerPositions(byte player)
	{
		List<String> playerPositions = new ArrayList<String>();
		
		for(int i = 0; i < CELL_COUNT; i++)
		{
			if(getCell(i * 2) == player)
			{
				playerPositions.add(convert(i * 2));
			}
		}
		
		return playerPositions;
	}
	
	public byte whoWon()
	{
		for(int i = 0; i < CELL_COUNT; i++)
		{
			if(getCell(CELL_COUNT * 2 - i * 2) == WHITE) return WHITE;
			if(getCell(i * 2) == BLACK) return BLACK;
		}
		
		return EMPTY;
	}
	
	/**
	 * Calcule un score associé à la disposition des pièces sur le jeu.
	 * 
	 * @return Le score associé à la grille de jeu
	 */
	public int evaluate(byte turn)
	{
		int score = 0;
		int value = 0;
		
		for(int i = 0; i < CELL_COUNT; i++)
		{
			value = new Surroundings(this, i * 2, turn).getValue();
			
			if(value == Surroundings.WIN) return value;
			
			if(getCell(i * 2) == currentPlayer)
			{
				score += value;
			}
			else if(getCell(i * 2) == (currentPlayer ^ 3))
			{
				score -= value << 3;
			}
		}
		
		return score;
	}
	
	public static String convert(int position)
	{
		return new String(new char[]{(char) (((position >> 1) % SIZE) + 1 + 64), (char) (((position >> 1) / SIZE) + 1 + 48)});
	}
	
	public static int convert(String position)
	{
		return ((position.charAt(0) - 1 - 64) << 1) + ((position.charAt(1) - 1 - 48) << 4);
	}
	
	public String toString()
	{
		String output = "  | A B C D E F G H\n-------------------\n";
		String line = "";
		
		for(int i = 0; i < CELL_COUNT; i++)
		{
			if(i % SIZE == 0)
			{
				output += (char) (9 - (i / SIZE + 1) + 48) + " | ";
			}
			
			switch(getCell((CELL_COUNT - 1 - i) * 2))
			{
				case WHITE:
					line = "x " + line;
					break;
				
				case BLACK:
					line = "o " + line;
					break;
				
				default:
				case EMPTY:
					line = ". " + line;
					break;
			}
			
			if((i + 1) % (SIZE) == 0)
			{
				output += line + "\n";
				line = "";
			}
		}
		
		return output;
	}
}
